
package com.mazaiting.auth.security.config;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import com.mazaiting.auth.domain.SecureIgnoreBean;
import com.mazaiting.auth.security.extension.mobile.SmsCodeAuthenticationProvider;
import com.mazaiting.auth.security.extension.wechat.WechatAuthenticationProvider;
import com.mazaiting.redis.service.IRedisStringService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * SpringSecurity 安全配置
 */
@Slf4j
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource(name = "sysUserDetailsService")
    private final UserDetailsService sysUserDetailsService;

    @Resource(name = "memberUserDetailsService")
    private UserDetailsService memberUserDetailsService;

    private final IRedisStringService redisStringService;

    private final SecureIgnoreBean secureIgnoreBean;

    private final WxMaService wxMaService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 允许跨域的 url
        List<String> urls = secureIgnoreBean.getUrls();
        if (CollectionUtil.isEmpty(urls)) {
            log.warn("未配置跨域列表");
            urls = new ArrayList<>();
        }
        http
                .authorizeRequests()
                .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
                .antMatchers(
//                        "/auth/oauth/publicKey",
//                        "/auth/v1/oauth/**",
                        Convert.toStrArray(urls)
                ).permitAll()
                .anyRequest().authenticated()
                // 禁用csrf
                .and().csrf().disable();
    }

    /**
     * 如果不配置 SpringBoot 会自动配置一个 AuthenticationManager 覆盖掉内存中的用户
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(daoAuthenticationProvider())
                .authenticationProvider(smsCodeAuthenticationProvider())
                .authenticationProvider(wechatAuthenticationProvider())
        ;
    }

    /**
     * 手机验证码认证授权提供者
     *
     * @return 手机验证码认证授权提供者
     */
    @Bean
    public SmsCodeAuthenticationProvider smsCodeAuthenticationProvider() {
        SmsCodeAuthenticationProvider provider = new SmsCodeAuthenticationProvider();
        provider.setUserDetailsService(sysUserDetailsService);
        provider.setRedisStringService(redisStringService);
        return provider;
    }

    /**
     * 用户名密码认证授权提供者
     *
     * @return 用户名密码认证授权提供者
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(sysUserDetailsService);
        provider.setPasswordEncoder(passwordEncoder());
        // 是否隐藏用户不存在异常，默认:true-隐藏；false-抛出异常；
        provider.setHideUserNotFoundExceptions(false);
        return provider;
    }

    /**
     * 微信认证授权提供者
     *
     * @return 微信认证授权提供者
     */
    @Bean
    public WechatAuthenticationProvider wechatAuthenticationProvider() {
        WechatAuthenticationProvider provider = new WechatAuthenticationProvider();
        provider.setUserDetailsService(memberUserDetailsService);
        provider.setWxMaService(wxMaService);
        return provider;
    }

    /**
     * 密码编码器
     * <p>
     * 委托方式，根据密码的前缀选择对应的encoder，例如：{bcypt}前缀->标识BCYPT算法加密；{noop}->标识不使用任何加密即明文的方式
     * 密码判读 DaoAuthenticationProvider#additionalAuthenticationChecks
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

}
